package zkzszd.opengl;
/** 
 * @author  zkzszd E-mail:lnb@love.com
 * @date 创建时间：2017-12-8 下午4:54:43 
 * @version 1.0 
 * @parameter  
 * @since  
 * @return  
 * 写java代码（主要是为了jni里面的代码回调java的代码实现
 */
import java.util.concurrent.locks.ReentrantLock;  
import javax.microedition.khronos.egl.EGL10;  
import javax.microedition.khronos.egl.EGLConfig;  
import javax.microedition.khronos.egl.EGLContext;  
import javax.microedition.khronos.egl.EGLDisplay;  
import javax.microedition.khronos.opengles.GL10;  
  
import android.app.ActivityManager;  
import android.content.Context;  
import android.content.pm.ConfigurationInfo;  
import android.graphics.PixelFormat;  
import android.opengl.GLSurfaceView;  
import android.util.AttributeSet;
import android.util.Log;  
  
public class AndroidGLES20 extends GLSurfaceView implements GLSurfaceView.Renderer  
{  
    private static String TAG = "zkzszd_Android_GLES";  
    private static final boolean DEBUG = false;  
    // True if onSurfaceCreated has been called.  
    private boolean surfaceCreated = false;  
    private boolean openGLCreated = false;  
    // True if NativeFunctionsRegistered has been called.  
    private boolean nativeFunctionsRegisted = false;  
    private ReentrantLock nativeFunctionLock = new ReentrantLock();  
    // Address of Native object that will do the drawing.  
    private long nativeObject = 0;  
    private int viewWidth = 0;  
    private int viewHeight = 0;  
  
    public static boolean UseOpenGL2(Object renderWindow) {  
        return AndroidGLES20.class.isInstance(renderWindow);  
    }  
  
    public AndroidGLES20(Context context) {  
        super(context);  
        init(false, 0, 0);  
    } 
    
    //xml是通过该构造函数构造的，有此构造函数才能在xml中使用
    public AndroidGLES20(Context context, AttributeSet attrs)
    {
    	 super(context, attrs); 
    	 if(!IsSupported(context))
    	 {
    		 Log.e(TAG, "this drive not support openglse2.0");
    	 }
    	 init(false, 0, 0);
    }
  
    public AndroidGLES20(Context context, boolean translucent,  
            int depth, int stencil) {  
        super(context);  
        init(translucent, depth, stencil);  
    }  
  
    //translucent 透亮
    private void init(boolean translucent, int depth, int stencil) {  
  
        // By default, GLSurfaceView() creates a RGB_565 opaque surface.  
        // If we want a translucent one, we should change the surface's  
        // format here, using PixelFormat.TRANSLUCENT for GL Surfaces  
        // is interpreted as any 32-bit surface with alpha by SurfaceFlinger.  
        if (translucent) {  
            this.getHolder().setFormat(PixelFormat.TRANSLUCENT);  
        }  
  
        // Setup the context factory for 2.0 rendering.  
        // See ContextFactory class definition below  
        setEGLContextFactory(new ContextFactory());  
  
        // We need to choose an EGLConfig that matches the format of  
        // our surface exactly. This is going to be done in our  
        // custom config chooser. See ConfigChooser class definition  
        // below.  
        setEGLConfigChooser( translucent ?  
                             new ConfigChooser(8, 8, 8, 8, depth, stencil) :  
                             new ConfigChooser(5, 6, 5, 0, depth, stencil) );  
  
        // Set the renderer responsible for frame rendering  
        this.setRenderer(this);  
        this.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);  
    }  
  
    private static class ContextFactory implements GLSurfaceView.EGLContextFactory {  
        private static int EGL_CONTEXT_CLIENT_VERSION = 0x3098;  
        public EGLContext createContext(EGL10 egl, EGLDisplay display, EGLConfig eglConfig) {  
            Log.w(TAG, "creating OpenGL ES 2.0 context");  
            checkEglError("Before eglCreateContext", egl);  
            int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL10.EGL_NONE };  
            EGLContext context = egl.eglCreateContext(display, eglConfig,  
                    EGL10.EGL_NO_CONTEXT, attrib_list);  
            checkEglError("After eglCreateContext", egl);  
            return context;  
        }  
  
        public void destroyContext(EGL10 egl, EGLDisplay display, EGLContext context) {  
            egl.eglDestroyContext(display, context);  
        }  
    }  
  
    private static void checkEglError(String prompt, EGL10 egl) {  
        int error;  
        while ((error = egl.eglGetError()) != EGL10.EGL_SUCCESS) {  
            Log.e(TAG, String.format("%s: EGL error: 0x%x", prompt, error));  
        }  
    }  
  
    private static class ConfigChooser implements GLSurfaceView.EGLConfigChooser {  
  
        public ConfigChooser(int r, int g, int b, int a, int depth, int stencil) {  
            mRedSize = r;  
            mGreenSize = g;  
            mBlueSize = b;  
            mAlphaSize = a;  
            mDepthSize = depth;  
            mStencilSize = stencil;  
        }  
  
        // This EGL config specification is used to specify 2.0 rendering.  
        // We use a minimum size of 4 bits for red/green/blue, but will  
        // perform actual matching in chooseConfig() below.  
        private static int EGL_OPENGL_ES2_BIT = 4;  
        private static int[] s_configAttribs2 =  
        {  
            EGL10.EGL_RED_SIZE, 4,  
            EGL10.EGL_GREEN_SIZE, 4,  
            EGL10.EGL_BLUE_SIZE, 4,  
            EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,  
            EGL10.EGL_NONE  
        };  
  
        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {  
  
            // Get the number of minimally matching EGL configurations  
            int[] num_config = new int[1];  
            egl.eglChooseConfig(display, s_configAttribs2, null, 0, num_config);  
  
            int numConfigs = num_config[0];  
  
            if (numConfigs <= 0) {  
                throw new IllegalArgumentException("No configs match configSpec");  
            }  
  
            // Allocate then read the array of minimally matching EGL configs  
            EGLConfig[] configs = new EGLConfig[numConfigs];  
            egl.eglChooseConfig(display, s_configAttribs2, configs, numConfigs, num_config);  
  
            if (DEBUG) {  
                printConfigs(egl, display, configs);  
            }  
            // Now return the "best" one  
            return chooseConfig(egl, display, configs);  
        }  
  
        public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,  
                EGLConfig[] configs) {  
            for(EGLConfig config : configs) {  
                int d = findConfigAttrib(egl, display, config,  
                        EGL10.EGL_DEPTH_SIZE, 0);  
                int s = findConfigAttrib(egl, display, config,  
                        EGL10.EGL_STENCIL_SIZE, 0);  
  
                // We need at least mDepthSize and mStencilSize bits  
                if (d < mDepthSize || s < mStencilSize)  
                    continue;  
  
                // We want an *exact* match for red/green/blue/alpha  
                int r = findConfigAttrib(egl, display, config,  
                        EGL10.EGL_RED_SIZE, 0);  
                int g = findConfigAttrib(egl, display, config,  
                            EGL10.EGL_GREEN_SIZE, 0);  
                int b = findConfigAttrib(egl, display, config,  
                            EGL10.EGL_BLUE_SIZE, 0);  
                int a = findConfigAttrib(egl, display, config,  
                        EGL10.EGL_ALPHA_SIZE, 0);  
  
                if (r == mRedSize && g == mGreenSize && b == mBlueSize && a == mAlphaSize)  
                    return config;  
            }  
            return null;  
        }  
  
        private int findConfigAttrib(EGL10 egl, EGLDisplay display,  
                EGLConfig config, int attribute, int defaultValue) {  
  
            if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {  
                return mValue[0];  
            }  
            return defaultValue;  
        }  
  
        private void printConfigs(EGL10 egl, EGLDisplay display,  
            EGLConfig[] configs) {  
            int numConfigs = configs.length;  
            Log.w(TAG, String.format("%d configurations", numConfigs));  
            for (int i = 0; i < numConfigs; i++) {  
                Log.w(TAG, String.format("Configuration %d:\n", i));  
                printConfig(egl, display, configs[i]);  
            }  
        }  
  
        private void printConfig(EGL10 egl, EGLDisplay display,  
                EGLConfig config) {  
            int[] attributes = {  
                    EGL10.EGL_BUFFER_SIZE,  
                    EGL10.EGL_ALPHA_SIZE,  
                    EGL10.EGL_BLUE_SIZE,  
                    EGL10.EGL_GREEN_SIZE,  
                    EGL10.EGL_RED_SIZE,  
                    EGL10.EGL_DEPTH_SIZE,  
                    EGL10.EGL_STENCIL_SIZE,  
                    EGL10.EGL_CONFIG_CAVEAT,  
                    EGL10.EGL_CONFIG_ID,  
                    EGL10.EGL_LEVEL,  
                    EGL10.EGL_MAX_PBUFFER_HEIGHT,  
                    EGL10.EGL_MAX_PBUFFER_PIXELS,  
                    EGL10.EGL_MAX_PBUFFER_WIDTH,  
                    EGL10.EGL_NATIVE_RENDERABLE,  
                    EGL10.EGL_NATIVE_VISUAL_ID,  
                    EGL10.EGL_NATIVE_VISUAL_TYPE,  
                    0x3030, // EGL10.EGL_PRESERVED_RESOURCES,  
                    EGL10.EGL_SAMPLES,  
                    EGL10.EGL_SAMPLE_BUFFERS,  
                    EGL10.EGL_SURFACE_TYPE,  
                    EGL10.EGL_TRANSPARENT_TYPE,  
                    EGL10.EGL_TRANSPARENT_RED_VALUE,  
                    EGL10.EGL_TRANSPARENT_GREEN_VALUE,  
                    EGL10.EGL_TRANSPARENT_BLUE_VALUE,  
                    0x3039, // EGL10.EGL_BIND_TO_TEXTURE_RGB,  
                    0x303A, // EGL10.EGL_BIND_TO_TEXTURE_RGBA,  
                    0x303B, // EGL10.EGL_MIN_SWAP_INTERVAL,  
                    0x303C, // EGL10.EGL_MAX_SWAP_INTERVAL,  
                    EGL10.EGL_LUMINANCE_SIZE,  
                    EGL10.EGL_ALPHA_MASK_SIZE,  
                    EGL10.EGL_COLOR_BUFFER_TYPE,  
                    EGL10.EGL_RENDERABLE_TYPE,  
                    0x3042 // EGL10.EGL_CONFORMANT  
            };  
            String[] names = {  
                    "EGL_BUFFER_SIZE",  
                    "EGL_ALPHA_SIZE",  
                    "EGL_BLUE_SIZE",  
                    "EGL_GREEN_SIZE",  
                    "EGL_RED_SIZE",  
                    "EGL_DEPTH_SIZE",  
                    "EGL_STENCIL_SIZE",  
                    "EGL_CONFIG_CAVEAT",  
                    "EGL_CONFIG_ID",  
                    "EGL_LEVEL",  
                    "EGL_MAX_PBUFFER_HEIGHT",  
                    "EGL_MAX_PBUFFER_PIXELS",  
                    "EGL_MAX_PBUFFER_WIDTH",  
                    "EGL_NATIVE_RENDERABLE",  
                    "EGL_NATIVE_VISUAL_ID",  
                    "EGL_NATIVE_VISUAL_TYPE",  
                    "EGL_PRESERVED_RESOURCES",  
                    "EGL_SAMPLES",  
                    "EGL_SAMPLE_BUFFERS",  
                    "EGL_SURFACE_TYPE",  
                    "EGL_TRANSPARENT_TYPE",  
                    "EGL_TRANSPARENT_RED_VALUE",  
                    "EGL_TRANSPARENT_GREEN_VALUE",  
                    "EGL_TRANSPARENT_BLUE_VALUE",  
                    "EGL_BIND_TO_TEXTURE_RGB",  
                    "EGL_BIND_TO_TEXTURE_RGBA",  
                    "EGL_MIN_SWAP_INTERVAL",  
                    "EGL_MAX_SWAP_INTERVAL",  
                    "EGL_LUMINANCE_SIZE",  
                    "EGL_ALPHA_MASK_SIZE",  
                    "EGL_COLOR_BUFFER_TYPE",  
                    "EGL_RENDERABLE_TYPE",  
                    "EGL_CONFORMANT"  
            };  
            int[] value = new int[1];  
            for (int i = 0; i < attributes.length; i++) {  
                int attribute = attributes[i];  
                String name = names[i];  
                if (egl.eglGetConfigAttrib(display, config, attribute, value)) {  
                    Log.w(TAG, String.format("  %s: %d\n", name, value[0]));  
                } else {  
                    // Log.w(TAG, String.format("  %s: failed\n", name));  
                    while (egl.eglGetError() != EGL10.EGL_SUCCESS);  
                }  
            }  
        }  
  
        // Subclasses can adjust these values:  
        protected int mRedSize;  
        protected int mGreenSize;  
        protected int mBlueSize;  
        protected int mAlphaSize;  
        protected int mDepthSize;  
        protected int mStencilSize;  
        private int[] mValue = new int[1];  
    }  
  
    // IsSupported  
    // Return true if this device support Open GL ES 2.0 rendering.  
    public static boolean IsSupported(Context context) {  
        ActivityManager am =  
                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);  
        ConfigurationInfo info = am.getDeviceConfigurationInfo();  
        if(info.reqGlEsVersion >= 0x20000) {  
            // Open GL ES 2.0 is supported.  
            return true;  
        }  
        return false;  
    }  
  
    public void onDrawFrame(GL10 gl) { 
        nativeFunctionLock.lock();  
        if(!nativeFunctionsRegisted || !surfaceCreated) {  
            nativeFunctionLock.unlock();  
            return;  
        }  
  
        if(!openGLCreated) {  
            if(0 != CreateOpenGLNative(nativeObject, viewWidth, viewHeight)) {  
                return; // Failed to create OpenGL  
            }  
            openGLCreated = true; // Created OpenGL successfully  
        }  
        DrawNative(nativeObject); // Draw the new frame  
        nativeFunctionLock.unlock();  
    } 
    
    public void Registe()
    {
    	
    }
  
    public void onSurfaceChanged(GL10 gl, int width, int height) { 
        surfaceCreated = true;  
        viewWidth = width;  
        viewHeight = height;  
  
        nativeFunctionLock.lock();  
        if(nativeFunctionsRegisted) 
        {  
            if(CreateOpenGLNative(nativeObject,width,height) == 0)
            {
                openGLCreated = true;  
            }
        }  
        nativeFunctionLock.unlock();  
    }  
  
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {  
    }  
  
    public void RegisterNativeObject(long nativeObject) {  
        nativeFunctionLock.lock();  
        this.nativeObject = nativeObject;  
        nativeFunctionsRegisted = true;  
        nativeFunctionLock.unlock();  
    }  
  
    public void DeRegisterNativeObject() {  
        nativeFunctionLock.lock();  
        nativeFunctionsRegisted = false;  
        openGLCreated = false;  
        this.nativeObject = 0;  
        nativeFunctionLock.unlock();  
    }  
  
    public void ReDraw() {// jni层解码以后的数据回调，然后由系统调用onDrawFrame显示  
        if(surfaceCreated) {  
            // Request the renderer to redraw using the render thread context.  
            this.requestRender();  
        }  
    }  
  
    private native int CreateOpenGLNative(long nativeObject, int width, int height);  
    private native void DrawNative(long nativeObject);  
}  